//credits to garthor

obj/gettable/stackable
    //the maximum number of items that can be stacked together, 0 for unlimited
    //how many items are in this stack
    //verbs for all items

    proc
        //here is our proc to stack
        stack()
            //loop through all items in the same location
            for(var/obj/gettable/stackable/I in loc)
                //check that we can stack with I
                if(I.canstack(src))
                    //find how many we can move from src to I
                    var/N
                    //only up to stacksize if it's nonzero
                    if(I.stacksize)
                        N = min(amount, I.stacksize-I.amount)
                    //as many as we have if stacksize is zero (and thus unlimited)
                    else
                        N = amount

                    amount -= N
                    I.amount += N
                    //update I's suffix
                    I.update()

                    //delete src if we're empty
                    if(amount == 0)
                        del(src)
            //call update() to update our suffix
            update()

        //this lets us know if we can stack with another item
        //I is new item being added, and src is the object it is trying to merge into
        canstack(var/obj/gettable/stackable/I)
            //by default we just return 1 if we're the same type
            //we can override this for different cases, though
            //IE: return 0 if some variables don't match
            //or return 1 even if types don't match (picking up ammo and stacking with a clip)
            return (I.type == src.type)

        //this is called whenever we update the amount in a stack
        update()
            //update our suffix to show how many are in this stack
            suffix = "[amount]"
            //oh, and let's use the gender variable too
            if(amount > 1)
                gender = "plural"
            else
                gender = "neuter"
            //this is available to be overridden to display the amount differently

        //here is a proc to delete N items from a stack
        remove(var/N)
            //if we don't have that many, return 0
            if(amount < N)
                return 0
            //if we have precisely that many
            else if(amount == N)
                //we have to use the default return value variable . to return something here:
                . = 1
                //because we're deleting the object, and we can't do anything after that delete
                del(src)
                //we are, however, returning a true value (indicating success), so we'll put this here so we know that
                return 1
                //the line is never actually executed
            //in most cases, N will be less than amount, so we just subtract N from amount and carry on
            else
                amount -= N
                update()
                return 1

    //whenever we're moved somewhere
    Move(loc, dir, var/nostack = 0)
        //perform the default move action
        .=..()
        //if the move was successful and we aren't not stacking
        if(. && !nostack)
            //stack with anything in our new location
            stack()

    //also, try to stack when we're created
    New(Loc, var/amount = 0, var/nostack = 0)
        ..()
        //if we specified an amount, set our amount to that.  Otherwise, just leave it as the default
        if(amount)
            src.amount = amount
        //stack if we didn't specify not to
        if(!nostack)
            stack()
        //if we don't call stack(), we'll need to call update() (stack() would have done it for us)
        else
            update()